// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/appcache/appcache_host.h"

#include <stdint.h>

#include <memory>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/message_loop/message_loop.h"
#include "content/browser/appcache/appcache.h"
#include "content/browser/appcache/appcache_backend_impl.h"
#include "content/browser/appcache/appcache_group.h"
#include "content/browser/appcache/mock_appcache_policy.h"
#include "content/browser/appcache/mock_appcache_service.h"
#include "net/url_request/url_request.h"
#include "storage/browser/quota/quota_manager.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace content {

class AppCacheHostTest : public testing::Test {
public:
    AppCacheHostTest()
    {
        get_status_callback_ = base::Bind(&AppCacheHostTest::GetStatusCallback,
            base::Unretained(this));
        start_update_callback_ = base::Bind(&AppCacheHostTest::StartUpdateCallback,
            base::Unretained(this));
        swap_cache_callback_ = base::Bind(&AppCacheHostTest::SwapCacheCallback,
            base::Unretained(this));
    }

    class MockFrontend : public AppCacheFrontend {
    public:
        MockFrontend()
            : last_host_id_(-222)
            , last_cache_id_(-222)
            , last_status_(APPCACHE_STATUS_OBSOLETE)
            , last_status_changed_(APPCACHE_STATUS_OBSOLETE)
            , last_event_id_(APPCACHE_OBSOLETE_EVENT)
            , content_blocked_(false)
        {
        }

        void OnCacheSelected(int host_id, const AppCacheInfo& info) override
        {
            last_host_id_ = host_id;
            last_cache_id_ = info.cache_id;
            last_status_ = info.status;
        }

        void OnStatusChanged(const std::vector<int>& host_ids,
            AppCacheStatus status) override
        {
            last_status_changed_ = status;
        }

        void OnEventRaised(const std::vector<int>& host_ids,
            AppCacheEventID event_id) override
        {
            last_event_id_ = event_id;
        }

        void OnErrorEventRaised(const std::vector<int>& host_ids,
            const AppCacheErrorDetails& details) override
        {
            last_event_id_ = APPCACHE_ERROR_EVENT;
        }

        void OnProgressEventRaised(const std::vector<int>& host_ids,
            const GURL& url,
            int num_total,
            int num_complete) override
        {
            last_event_id_ = APPCACHE_PROGRESS_EVENT;
        }

        void OnLogMessage(int host_id,
            AppCacheLogLevel log_level,
            const std::string& message) override { }

        void OnContentBlocked(int host_id, const GURL& manifest_url) override
        {
            content_blocked_ = true;
        }

        int last_host_id_;
        int64_t last_cache_id_;
        AppCacheStatus last_status_;
        AppCacheStatus last_status_changed_;
        AppCacheEventID last_event_id_;
        bool content_blocked_;
    };

    class MockQuotaManagerProxy : public storage::QuotaManagerProxy {
    public:
        MockQuotaManagerProxy()
            : QuotaManagerProxy(NULL, NULL)
        {
        }

        // Not needed for our tests.
        void RegisterClient(storage::QuotaClient* client) override { }
        void NotifyStorageAccessed(storage::QuotaClient::ID client_id,
            const GURL& origin,
            storage::StorageType type) override { }
        void NotifyStorageModified(storage::QuotaClient::ID client_id,
            const GURL& origin,
            storage::StorageType type,
            int64_t delta) override { }
        void SetUsageCacheEnabled(storage::QuotaClient::ID client_id,
            const GURL& origin,
            storage::StorageType type,
            bool enabled) override { }
        void GetUsageAndQuota(base::SequencedTaskRunner* original_task_runner,
            const GURL& origin,
            storage::StorageType type,
            const GetUsageAndQuotaCallback& callback) override { }

        void NotifyOriginInUse(const GURL& origin) override { inuse_[origin] += 1; }

        void NotifyOriginNoLongerInUse(const GURL& origin) override
        {
            inuse_[origin] -= 1;
        }

        int GetInUseCount(const GURL& origin)
        {
            return inuse_[origin];
        }

        void reset()
        {
            inuse_.clear();
        }

        // Map from origin to count of inuse notifications.
        std::map<GURL, int> inuse_;

    protected:
        ~MockQuotaManagerProxy() override { }
    };

    void GetStatusCallback(AppCacheStatus status, void* param)
    {
        last_status_result_ = status;
        last_callback_param_ = param;
    }

    void StartUpdateCallback(bool result, void* param)
    {
        last_start_result_ = result;
        last_callback_param_ = param;
    }

    void SwapCacheCallback(bool result, void* param)
    {
        last_swap_result_ = result;
        last_callback_param_ = param;
    }

    base::MessageLoop message_loop_;

    // Mock classes for the 'host' to work with
    MockAppCacheService service_;
    MockFrontend mock_frontend_;

    // Mock callbacks we expect to receive from the 'host'
    content::GetStatusCallback get_status_callback_;
    content::StartUpdateCallback start_update_callback_;
    content::SwapCacheCallback swap_cache_callback_;

    AppCacheStatus last_status_result_;
    bool last_swap_result_;
    bool last_start_result_;
    void* last_callback_param_;
};

TEST_F(AppCacheHostTest, Basic)
{
    // Construct a host and test what state it appears to be in.
    AppCacheHost host(1, &mock_frontend_, &service_);
    EXPECT_EQ(1, host.host_id());
    EXPECT_EQ(&service_, host.service());
    EXPECT_EQ(&mock_frontend_, host.frontend());
    EXPECT_EQ(NULL, host.associated_cache());
    EXPECT_FALSE(host.is_selection_pending());

    // See that the callbacks are delivered immediately
    // and respond as if there is no cache selected.
    last_status_result_ = APPCACHE_STATUS_OBSOLETE;
    host.GetStatusWithCallback(get_status_callback_, reinterpret_cast<void*>(1));
    EXPECT_EQ(APPCACHE_STATUS_UNCACHED, last_status_result_);
    EXPECT_EQ(reinterpret_cast<void*>(1), last_callback_param_);

    last_start_result_ = true;
    host.StartUpdateWithCallback(start_update_callback_,
        reinterpret_cast<void*>(2));
    EXPECT_FALSE(last_start_result_);
    EXPECT_EQ(reinterpret_cast<void*>(2), last_callback_param_);

    last_swap_result_ = true;
    host.SwapCacheWithCallback(swap_cache_callback_, reinterpret_cast<void*>(3));
    EXPECT_FALSE(last_swap_result_);
    EXPECT_EQ(reinterpret_cast<void*>(3), last_callback_param_);
}

TEST_F(AppCacheHostTest, SelectNoCache)
{
    scoped_refptr<MockQuotaManagerProxy> mock_quota_proxy(
        new MockQuotaManagerProxy);
    service_.set_quota_manager_proxy(mock_quota_proxy.get());

    // Reset our mock frontend
    mock_frontend_.last_cache_id_ = -333;
    mock_frontend_.last_host_id_ = -333;
    mock_frontend_.last_status_ = APPCACHE_STATUS_OBSOLETE;

    const GURL kDocAndOriginUrl(GURL("http://whatever/").GetOrigin());
    {
        AppCacheHost host(1, &mock_frontend_, &service_);
        host.SelectCache(kDocAndOriginUrl, kAppCacheNoCacheId, GURL());
        EXPECT_EQ(1, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl));

        // We should have received an OnCacheSelected msg
        EXPECT_EQ(1, mock_frontend_.last_host_id_);
        EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_);
        EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_);

        // Otherwise, see that it respond as if there is no cache selected.
        EXPECT_EQ(1, host.host_id());
        EXPECT_EQ(&service_, host.service());
        EXPECT_EQ(&mock_frontend_, host.frontend());
        EXPECT_EQ(NULL, host.associated_cache());
        EXPECT_FALSE(host.is_selection_pending());
        EXPECT_TRUE(host.preferred_manifest_url().is_empty());
    }
    EXPECT_EQ(0, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl));
    service_.set_quota_manager_proxy(NULL);
}

TEST_F(AppCacheHostTest, ForeignEntry)
{
    // Reset our mock frontend
    mock_frontend_.last_cache_id_ = -333;
    mock_frontend_.last_host_id_ = -333;
    mock_frontend_.last_status_ = APPCACHE_STATUS_OBSOLETE;

    // Precondition, a cache with an entry that is not marked as foreign.
    const int kCacheId = 22;
    const GURL kDocumentURL("http://origin/document");
    scoped_refptr<AppCache> cache = new AppCache(service_.storage(), kCacheId);
    cache->AddEntry(kDocumentURL, AppCacheEntry(AppCacheEntry::EXPLICIT));

    AppCacheHost host(1, &mock_frontend_, &service_);
    host.MarkAsForeignEntry(kDocumentURL, kCacheId);

    // We should have received an OnCacheSelected msg for kAppCacheNoCacheId.
    EXPECT_EQ(1, mock_frontend_.last_host_id_);
    EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_);
    EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_);

    // See that it respond as if there is no cache selected.
    EXPECT_EQ(1, host.host_id());
    EXPECT_EQ(&service_, host.service());
    EXPECT_EQ(&mock_frontend_, host.frontend());
    EXPECT_EQ(NULL, host.associated_cache());
    EXPECT_FALSE(host.is_selection_pending());

    // See that the entry was marked as foreign.
    EXPECT_TRUE(cache->GetEntry(kDocumentURL)->IsForeign());
}

TEST_F(AppCacheHostTest, ForeignFallbackEntry)
{
    // Reset our mock frontend
    mock_frontend_.last_cache_id_ = -333;
    mock_frontend_.last_host_id_ = -333;
    mock_frontend_.last_status_ = APPCACHE_STATUS_OBSOLETE;

    // Precondition, a cache with a fallback entry that is not marked as foreign.
    const int kCacheId = 22;
    const GURL kFallbackURL("http://origin/fallback_resource");
    scoped_refptr<AppCache> cache = new AppCache(service_.storage(), kCacheId);
    cache->AddEntry(kFallbackURL, AppCacheEntry(AppCacheEntry::FALLBACK));

    AppCacheHost host(1, &mock_frontend_, &service_);
    host.NotifyMainResourceIsNamespaceEntry(kFallbackURL);
    host.MarkAsForeignEntry(GURL("http://origin/missing_document"), kCacheId);

    // We should have received an OnCacheSelected msg for kAppCacheNoCacheId.
    EXPECT_EQ(1, mock_frontend_.last_host_id_);
    EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_);
    EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_);

    // See that the fallback entry was marked as foreign.
    EXPECT_TRUE(cache->GetEntry(kFallbackURL)->IsForeign());
}

TEST_F(AppCacheHostTest, FailedCacheLoad)
{
    // Reset our mock frontend
    mock_frontend_.last_cache_id_ = -333;
    mock_frontend_.last_host_id_ = -333;
    mock_frontend_.last_status_ = APPCACHE_STATUS_OBSOLETE;

    AppCacheHost host(1, &mock_frontend_, &service_);
    EXPECT_FALSE(host.is_selection_pending());

    const int kMockCacheId = 333;

    // Put it in a state where we're waiting on a cache
    // load prior to finishing cache selection.
    host.pending_selected_cache_id_ = kMockCacheId;
    EXPECT_TRUE(host.is_selection_pending());

    // The callback should not occur until we finish cache selection.
    last_status_result_ = APPCACHE_STATUS_OBSOLETE;
    last_callback_param_ = reinterpret_cast<void*>(-1);
    host.GetStatusWithCallback(get_status_callback_, reinterpret_cast<void*>(1));
    EXPECT_EQ(APPCACHE_STATUS_OBSOLETE, last_status_result_);
    EXPECT_EQ(reinterpret_cast<void*>(-1), last_callback_param_);

    // Satisfy the load with NULL, a failure.
    host.OnCacheLoaded(NULL, kMockCacheId);

    // Cache selection should have finished
    EXPECT_FALSE(host.is_selection_pending());
    EXPECT_EQ(1, mock_frontend_.last_host_id_);
    EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_);
    EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_);

    // Callback should have fired upon completing the cache load too.
    EXPECT_EQ(APPCACHE_STATUS_UNCACHED, last_status_result_);
    EXPECT_EQ(reinterpret_cast<void*>(1), last_callback_param_);
}

TEST_F(AppCacheHostTest, FailedGroupLoad)
{
    AppCacheHost host(1, &mock_frontend_, &service_);

    const GURL kMockManifestUrl("http://foo.bar/baz");

    // Put it in a state where we're waiting on a cache
    // load prior to finishing cache selection.
    host.pending_selected_manifest_url_ = kMockManifestUrl;
    EXPECT_TRUE(host.is_selection_pending());

    // The callback should not occur until we finish cache selection.
    last_status_result_ = APPCACHE_STATUS_OBSOLETE;
    last_callback_param_ = reinterpret_cast<void*>(-1);
    host.GetStatusWithCallback(get_status_callback_, reinterpret_cast<void*>(1));
    EXPECT_EQ(APPCACHE_STATUS_OBSOLETE, last_status_result_);
    EXPECT_EQ(reinterpret_cast<void*>(-1), last_callback_param_);

    // Satisfy the load will NULL, a failure.
    host.OnGroupLoaded(NULL, kMockManifestUrl);

    // Cache selection should have finished
    EXPECT_FALSE(host.is_selection_pending());
    EXPECT_EQ(1, mock_frontend_.last_host_id_);
    EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_);
    EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_);

    // Callback should have fired upon completing the group load.
    EXPECT_EQ(APPCACHE_STATUS_UNCACHED, last_status_result_);
    EXPECT_EQ(reinterpret_cast<void*>(1), last_callback_param_);
}

TEST_F(AppCacheHostTest, SetSwappableCache)
{
    AppCacheHost host(1, &mock_frontend_, &service_);
    host.SetSwappableCache(NULL);
    EXPECT_FALSE(host.swappable_cache_.get());

    scoped_refptr<AppCacheGroup> group1(new AppCacheGroup(
        service_.storage(), GURL(), service_.storage()->NewGroupId()));
    host.SetSwappableCache(group1.get());
    EXPECT_FALSE(host.swappable_cache_.get());

    AppCache* cache1 = new AppCache(service_.storage(), 111);
    cache1->set_complete(true);
    group1->AddCache(cache1);
    host.SetSwappableCache(group1.get());
    EXPECT_EQ(cache1, host.swappable_cache_.get());

    mock_frontend_.last_host_id_ = -222; // to verify we received OnCacheSelected

    host.AssociateCompleteCache(cache1);
    EXPECT_FALSE(host.swappable_cache_.get()); // was same as associated cache
    EXPECT_EQ(APPCACHE_STATUS_IDLE, host.GetStatus());
    // verify OnCacheSelected was called
    EXPECT_EQ(host.host_id(), mock_frontend_.last_host_id_);
    EXPECT_EQ(cache1->cache_id(), mock_frontend_.last_cache_id_);
    EXPECT_EQ(APPCACHE_STATUS_IDLE, mock_frontend_.last_status_);

    AppCache* cache2 = new AppCache(service_.storage(), 222);
    cache2->set_complete(true);
    group1->AddCache(cache2);
    EXPECT_EQ(cache2, host.swappable_cache_.get()); // updated to newest

    scoped_refptr<AppCacheGroup> group2(
        new AppCacheGroup(service_.storage(), GURL("http://foo.com"),
            service_.storage()->NewGroupId()));
    AppCache* cache3 = new AppCache(service_.storage(), 333);
    cache3->set_complete(true);
    group2->AddCache(cache3);

    AppCache* cache4 = new AppCache(service_.storage(), 444);
    cache4->set_complete(true);
    group2->AddCache(cache4);
    EXPECT_EQ(cache2, host.swappable_cache_.get()); // unchanged

    host.AssociateCompleteCache(cache3);
    EXPECT_EQ(cache4, host.swappable_cache_.get()); // newest cache in group2
    EXPECT_FALSE(group1->HasCache()); // both caches in group1 have refcount 0

    host.AssociateNoCache(GURL());
    EXPECT_FALSE(host.swappable_cache_.get());
    EXPECT_FALSE(group2->HasCache()); // both caches in group2 have refcount 0

    // Host adds reference to newest cache when an update is complete.
    AppCache* cache5 = new AppCache(service_.storage(), 555);
    cache5->set_complete(true);
    group2->AddCache(cache5);
    host.group_being_updated_ = group2;
    host.OnUpdateComplete(group2.get());
    EXPECT_FALSE(host.group_being_updated_.get());
    EXPECT_EQ(cache5, host.swappable_cache_.get());

    group2->RemoveCache(cache5);
    EXPECT_FALSE(group2->HasCache());
    host.group_being_updated_ = group2;
    host.OnUpdateComplete(group2.get());
    EXPECT_FALSE(host.group_being_updated_.get());
    EXPECT_FALSE(host.swappable_cache_.get()); // group2 had no newest cache
}

TEST_F(AppCacheHostTest, ForDedicatedWorker)
{
    const int kMockProcessId = 1;
    const int kParentHostId = 1;
    const int kWorkerHostId = 2;

    AppCacheBackendImpl backend_impl;
    backend_impl.Initialize(&service_, &mock_frontend_, kMockProcessId);
    backend_impl.RegisterHost(kParentHostId);
    backend_impl.RegisterHost(kWorkerHostId);

    AppCacheHost* parent_host = backend_impl.GetHost(kParentHostId);
    EXPECT_FALSE(parent_host->is_for_dedicated_worker());

    AppCacheHost* worker_host = backend_impl.GetHost(kWorkerHostId);
    worker_host->SelectCacheForWorker(kParentHostId, kMockProcessId);
    EXPECT_TRUE(worker_host->is_for_dedicated_worker());
    EXPECT_EQ(parent_host, worker_host->GetParentAppCacheHost());

    // We should have received an OnCacheSelected msg for the worker_host.
    // The host for workers always indicates 'no cache selected' regardless
    // of its parent's state. This is OK because the worker cannot access
    // the scriptable interface, the only function available is resource
    // loading (see appcache_request_handler_unittests those tests).
    EXPECT_EQ(kWorkerHostId, mock_frontend_.last_host_id_);
    EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_);
    EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_);

    // Simulate the parent being torn down.
    backend_impl.UnregisterHost(kParentHostId);
    parent_host = NULL;
    EXPECT_EQ(NULL, backend_impl.GetHost(kParentHostId));
    EXPECT_EQ(NULL, worker_host->GetParentAppCacheHost());
}

TEST_F(AppCacheHostTest, SelectCacheAllowed)
{
    scoped_refptr<MockQuotaManagerProxy> mock_quota_proxy(
        new MockQuotaManagerProxy);
    MockAppCachePolicy mock_appcache_policy;
    mock_appcache_policy.can_create_return_value_ = true;
    service_.set_quota_manager_proxy(mock_quota_proxy.get());
    service_.set_appcache_policy(&mock_appcache_policy);

    // Reset our mock frontend
    mock_frontend_.last_cache_id_ = -333;
    mock_frontend_.last_host_id_ = -333;
    mock_frontend_.last_status_ = APPCACHE_STATUS_OBSOLETE;
    mock_frontend_.last_event_id_ = APPCACHE_OBSOLETE_EVENT;
    mock_frontend_.content_blocked_ = false;

    const GURL kDocAndOriginUrl(GURL("http://whatever/").GetOrigin());
    const GURL kManifestUrl(GURL("http://whatever/cache.manifest"));
    {
        AppCacheHost host(1, &mock_frontend_, &service_);
        host.first_party_url_ = kDocAndOriginUrl;
        host.SelectCache(kDocAndOriginUrl, kAppCacheNoCacheId, kManifestUrl);
        EXPECT_EQ(1, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl));

        // MockAppCacheService::LoadOrCreateGroup is asynchronous, so we shouldn't
        // have received an OnCacheSelected msg yet.
        EXPECT_EQ(-333, mock_frontend_.last_host_id_);
        EXPECT_EQ(-333, mock_frontend_.last_cache_id_);
        EXPECT_EQ(APPCACHE_STATUS_OBSOLETE, mock_frontend_.last_status_);
        // No error events either
        EXPECT_EQ(APPCACHE_OBSOLETE_EVENT, mock_frontend_.last_event_id_);
        EXPECT_FALSE(mock_frontend_.content_blocked_);

        EXPECT_TRUE(host.is_selection_pending());
    }
    EXPECT_EQ(0, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl));
    service_.set_quota_manager_proxy(NULL);
}

TEST_F(AppCacheHostTest, SelectCacheBlocked)
{
    scoped_refptr<MockQuotaManagerProxy> mock_quota_proxy(
        new MockQuotaManagerProxy);
    MockAppCachePolicy mock_appcache_policy;
    mock_appcache_policy.can_create_return_value_ = false;
    service_.set_quota_manager_proxy(mock_quota_proxy.get());
    service_.set_appcache_policy(&mock_appcache_policy);

    // Reset our mock frontend
    mock_frontend_.last_cache_id_ = -333;
    mock_frontend_.last_host_id_ = -333;
    mock_frontend_.last_status_ = APPCACHE_STATUS_OBSOLETE;
    mock_frontend_.last_event_id_ = APPCACHE_OBSOLETE_EVENT;
    mock_frontend_.content_blocked_ = false;

    const GURL kDocAndOriginUrl(GURL("http://whatever/").GetOrigin());
    const GURL kManifestUrl(GURL("http://whatever/cache.manifest"));
    {
        AppCacheHost host(1, &mock_frontend_, &service_);
        host.first_party_url_ = kDocAndOriginUrl;
        host.SelectCache(kDocAndOriginUrl, kAppCacheNoCacheId, kManifestUrl);
        EXPECT_EQ(1, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl));

        // We should have received an OnCacheSelected msg
        EXPECT_EQ(1, mock_frontend_.last_host_id_);
        EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_);
        EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_);

        // Also, an error event was raised
        EXPECT_EQ(APPCACHE_ERROR_EVENT, mock_frontend_.last_event_id_);
        EXPECT_TRUE(mock_frontend_.content_blocked_);

        // Otherwise, see that it respond as if there is no cache selected.
        EXPECT_EQ(1, host.host_id());
        EXPECT_EQ(&service_, host.service());
        EXPECT_EQ(&mock_frontend_, host.frontend());
        EXPECT_EQ(NULL, host.associated_cache());
        EXPECT_FALSE(host.is_selection_pending());
        EXPECT_TRUE(host.preferred_manifest_url().is_empty());
    }
    EXPECT_EQ(0, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl));
    service_.set_quota_manager_proxy(NULL);
}

TEST_F(AppCacheHostTest, SelectCacheTwice)
{
    AppCacheHost host(1, &mock_frontend_, &service_);
    const GURL kDocAndOriginUrl(GURL("http://whatever/").GetOrigin());

    EXPECT_TRUE(host.SelectCache(kDocAndOriginUrl, kAppCacheNoCacheId, GURL()));

    // Select methods should bail if cache has already been selected.
    EXPECT_FALSE(host.SelectCache(kDocAndOriginUrl, kAppCacheNoCacheId, GURL()));
    EXPECT_FALSE(host.SelectCacheForWorker(0, 0));
    EXPECT_FALSE(host.SelectCacheForSharedWorker(kAppCacheNoCacheId));
    EXPECT_FALSE(host.MarkAsForeignEntry(kDocAndOriginUrl, kAppCacheNoCacheId));
}

} // namespace content
